/*****************************************************************************/
/*  misc.c - general purpose routines                                        */
/*  Copyright (C) 1998-2007 Brian Masney <masneyb@gftp.org>                  */
/*                                                                           */
/*  This program is free software; you can redistribute it and/or modify     */
/*  it under the terms of the GNU General Public License as published by     */
/*  the Free Software Foundation; either version 2 of the License, or        */
/*  (at your option) any later version.                                      */
/*                                                                           */
/*  This program is distributed in the hope that it will be useful,          */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
/*  GNU General Public License for more details.                             */
/*                                                                           */
/*  You should have received a copy of the GNU General Public License        */
/*  along with this program; if not, write to the Free Software              */
/*  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA      */
/*****************************************************************************/

#include "gftp.h"
#include "options.h"


int
copyfile (char *source, char *dest)
{
  int srcfd, destfd;
  char buf[8192];
  ssize_t n;

  if ((srcfd = gftp_fd_open (NULL, source, O_RDONLY, 0)) == -1)
    {
      printf (_("Error: Cannot open local file %s: %s\n"),
              source, g_strerror (errno));
      exit (EXIT_FAILURE);
    }

  if ((destfd = gftp_fd_open (NULL, dest, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) == -1)
    {
      printf (_("Error: Cannot open local file %s: %s\n"),
              dest, g_strerror (errno));
      close (srcfd);
      exit (EXIT_FAILURE);
    }

  while ((n = read (srcfd, buf, sizeof (buf))) > 0)
    {
      if (write (destfd, buf, n) == -1)
        {
          printf (_("Error: Could not write to socket: %s\n"), 
                  g_strerror (errno));
          exit (EXIT_FAILURE);
        }
    }

  if (n == -1)
    {
      printf (_("Error: Could not read from socket: %s\n"), g_strerror (errno));
      exit (EXIT_FAILURE);
    }

  if (close (srcfd) == -1)
    {
      printf (_("Error closing file descriptor: %s\n"), g_strerror (errno));
      exit (EXIT_FAILURE);
    }

  if (close (destfd) == -1)
    {
      printf (_("Error closing file descriptor: %s\n"), g_strerror (errno));
      exit (EXIT_FAILURE);
    }

  return (1);
}

char *
alltrim (char *str)
{
    char *pos, *newpos;
    int diff;

    pos = str + strlen (str) - 1;
    while (pos >= str && (*pos == ' ' || *pos == '\t'))
        *pos-- = '\0';

    pos = str;
    diff = 0;
    while (*pos++ == ' ')
        diff++;

    if (diff == 0)
        return (str);

    pos = str + diff;
    newpos = str;
    while (*pos != '\0')
        *newpos++ = *pos++;
    *newpos = '\0';

    return (str);
}

char*
gftp_expand_path (const char *src)
{
    char *str, *pos, *endpos, *prevpos, *newstr, *tempstr, *ntoken,
        tempchar;
    struct passwd *pw;
 
    pw = NULL;
    str = strdup (src);
 
    if (*str == '~')
    {
        if (*(str + 1) == '/' || *(str + 1) == '\0')
            pw = getpwuid (geteuid ());
        else
        {
            if ((pos = strchr (str, '/')) != NULL)
                *pos = '\0';
 
            pw = getpwnam (str + 1);
 
            if (pos != NULL)
                *pos = '/';
        }

        if (pw != NULL)
        {
            if ((pos = strchr (str, '/')) == NULL)
                newstr = g_strdup (pw->pw_dir);
            else
                newstr = g_build_path ("/", pw->pw_dir, pos, NULL);

            g_free (str);
            str = newstr;
        }
    }

    if (str[0] != '/')  {
       pos = strchr (str, '/');
       if (pos == NULL) {
           newstr = strdup (str);
           return newstr;
       } else {
           *pos = '\0';
           newstr = strdup (str);
           *pos = '/';
           endpos = pos;
       }
    } else {
        endpos = str;
        newstr= NULL;
    }

    while ((pos = strchr (endpos, '/')) != NULL)
    {
        pos++;
 
        for (ntoken = pos; *ntoken == '/'; ntoken++);
 
        if ((endpos = strchr (ntoken, '/')) == NULL)
            endpos = pos + strlen (pos);
 
        tempchar = *endpos;
        *endpos = '\0';
 
        if (strcmp (ntoken, "..") == 0)
        {
            if (newstr != NULL && (prevpos = strrchr (newstr, '/')) != NULL)
            {
                *prevpos = '\0';
                if (*newstr == '\0')
                {
                    g_free (newstr);
                    newstr = NULL;
                }
            }
        } 
        else if (strcmp (ntoken, ".") != 0)
        {
            if (newstr == NULL)
                newstr = strdup (pos - 1);
            else
            {
                tempstr = g_strconcat (newstr, pos - 1, NULL);
                g_free (newstr);
                newstr = tempstr;
            }
        }
 
        *endpos = tempchar;
        if (*endpos == '\0')
            break; 
    }
 
    if (endpos != NULL && *endpos != '\0' && newstr == NULL)
    {
        if (strcmp (endpos, "..") == 0)
            newstr = g_strdup ("/");
        else
            newstr = g_strdup (endpos);
    }
 
    if (newstr == NULL || *newstr == '\0')
    {
        if (newstr != NULL)
            g_free (newstr);
 
        newstr = g_strdup ("/");
    }
 
    g_free (str);
  
    return (newstr);
}

void
gftp_info (void)
{
    int i;

    printf ("%s\n", gftp_version);

#ifdef _REENTRANT
    printf ("#define _REENTRANT\n");
#endif

#ifdef _GNU_SOURCE
    printf ("#define _GNU_SOURCE\n");
#endif

#ifdef _LARGEFILE_SOURCE
    printf ("#define _LARGEFILE_SOURCE\n");
#endif

#ifdef _LARGEFILE64_SOURCE
    printf ("#define _LARGEFILE64_SOURCE\n");
#endif


#ifdef _FILE_OFFSET_BITS 
    printf ("#define _FILE_OFFSET_BITS %d\n", _FILE_OFFSET_BITS);
#endif

    printf ("sizeof (off_t) = %d\n", sizeof (off_t));

#ifdef HAVE_INTL_PRINTF 
    printf ("#define HAVE_INTL_PRINTF\n");
#endif

    printf ("GFTP_OFF_T_HEX_PRINTF_MOD = %s\n", GFTP_OFF_T_HEX_PRINTF_MOD);
    printf ("GFTP_OFF_T_INTL_PRINTF_MOD = %s\n", GFTP_OFF_T_INTL_PRINTF_MOD);
    printf ("GFTP_OFF_T_PRINTF_MOD = %s\n", GFTP_OFF_T_PRINTF_MOD);
    printf ("GFTP_OFF_T_11PRINTF_MOD = %s\n", GFTP_OFF_T_11PRINTF_MOD);

#ifdef HAVE_GETADDRINFO   
    printf ("#define HAVE_GETADDRINFO\n");
#endif

#ifdef HAVE_GAI_STRERROR
    printf ("#define HAVE_GAI_STRERROR\n");
#endif

#ifdef HAVE_GETDTABLESIZE
    printf ("#define HAVE_GETDTABLESIZE\n");
#endif

#ifdef G_HAVE_GINT64
    printf ("#define G_HAVE_GINT64\n");
#endif

#ifdef HAVE_LIBREADLINE
    printf ("#define HAVE_LIBREADLINE\n");
#endif

#ifdef ENABLE_NLS      
    printf ("#define ENABLE_NLS\n");
#endif

#ifdef HAVE_GETTEXT    
    printf ("#define HAVE_GETTEXT\n");
#endif

    printf ("glib version: %d.%d.%d\n", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION,
            GLIB_MICRO_VERSION);

    /* printf ("PTY implementation: %s\n", gftp_get_pty_impl ()); */

#ifdef USE_SSL         
    printf ("OpenSSL version: %s\n", OPENSSL_VERSION_TEXT);
#endif

    printf ("Enabled protocols: ");
    for (i=0; gftp_protocols[i].name != NULL; i++)
    {
        printf ("%s ", gftp_protocols[i].name);
    }
    printf ("\n");
}





gint
string_hash_compare (gconstpointer path1, gconstpointer path2)
{
    return (strcmp ((char *) path1, (char *) path2) == 0);
}


guint
string_hash_function (gconstpointer key)
{
    guint ret;
    int i;

    ret = 0;
    for (i=0; ((char *) key)[i] != '\0' && i < 3; i++)
        ret += ((char *) key)[i];

    return (ret);
}


gint
uint_hash_compare (gconstpointer path1, gconstpointer path2)
{
    return (GPOINTER_TO_UINT (path1) == GPOINTER_TO_UINT (path2));
}


guint
uint_hash_function (gconstpointer key)
{
    return (GPOINTER_TO_UINT (key));
}



int
compare_request (gftp_request * request1, gftp_request * request2,
                 int compare_dirs)
{
    char *strarr[3][2];
    int i, ret;

    ret = 1;
    if (request1->protonum == request2->protonum &&
        request1->port == request2->port)
    {
        strarr[0][0] = request1->hostname;
        strarr[0][1] = request2->hostname;
        strarr[1][0] = request1->username;
        strarr[1][1] = request2->username;
        if (compare_dirs)
        {
            strarr[2][0] = request1->directory;
            strarr[2][1] = request2->directory;
        }
        else
            strarr[2][0] = strarr[2][1] = "";

        for (i = 0; i < 3; i++)
        {
            if ((strarr[i][0] && !strarr[i][1]) ||
                (!strarr[i][0] && strarr[i][1]))
            {
                ret = 0;
                break;
            }

            if (strarr[i][0] && strarr[i][1] &&
                strcmp (strarr[i][0], strarr[i][1]) != 0)
            {
                ret = 0;
                break;
            }
        }
    }
    else
        ret = 0;
    return (ret);
}



gftp_request * 
gftp_copy_request (gftp_request * req)
{
    gftp_request * newreq;

    newreq = gftp_request_new (req->logging_function);

    if (req->hostname)
        newreq->hostname = g_strdup (req->hostname);
    if (req->username)
        newreq->username = g_strdup (req->username);
    if (req->password)
        newreq->password = g_strdup (req->password);
    if (req->account)
        newreq->account = g_strdup (req->account);
    if (req->directory)
        newreq->directory = g_strdup (req->directory);
    newreq->port = req->port;
    newreq->use_proxy = req->use_proxy;
    newreq->logging_function = req->logging_function;
    newreq->ai_family = req->ai_family;

    if (req->hostp)
    {
#if defined (HAVE_GETADDRINFO) && defined (HAVE_GAI_STRERROR)  

    struct addrinfo *hostp = req->hostp;
    struct addrinfo *newhostp = newreq->hostp;
      
    while (hostp != NULL)
    {
        newhostp = g_malloc (sizeof(struct addrinfo));
        memcpy (newhostp, hostp, sizeof (struct addrinfo));
        newhostp->ai_addr = g_malloc (sizeof (struct sockaddr));
        memcpy(newhostp->ai_addr, hostp->ai_addr, sizeof (struct sockaddr));
        if (hostp->ai_canonname)
            newhostp->ai_canonname = strdup(hostp->ai_canonname);

        if (req->current_hostp == hostp)
            newreq->current_hostp = newhostp;

        hostp = hostp->ai_next; newhostp = newhostp->ai_next;
    }
#else                                                 
    newreq->hostp = g_malloc (sizeof (struct hostent));
    memcpy(newreq->hostp, req->hostp, sizeof (struct hostent));
    newreq->host = req->host;
    newreq->curhost = req->curhost;
#endif                      
    } else {
        newreq->hostp = NULL;
    }

    newreq->free_hostp = 1;
    
    /* gftp_copy_local_options (&newreq->local_options_vars,  */
    /*                          &newreq->local_options_hash, */
    /*                          &newreq->num_local_options_vars, */
    /*                          req->local_options_vars, */
    /*                          req->num_local_options_vars); */

    if (req->init != NULL && req->init (newreq) < 0)
    {
        gftp_request_destroy (newreq, 1);
        return (NULL);
    }

    gftp_copy_param_options (newreq, req);

    return (newreq);
}


static gint
gftp_file_sort_function_as (gconstpointer a, gconstpointer b)
{
    const gftp_file * f1, * f2;

    f1 = a;
    f2 = b;
    return (strcasecmp (f1->file, f2->file));
}


static gint
gftp_file_sort_function_ds (gconstpointer a, gconstpointer b)
{
    const gftp_file * f1, * f2;
    gint ret;

    f1 = a;
    f2 = b;
    ret = strcasecmp (f1->file, f2->file);
    return (ret * -1);
}


static gint
gftp_user_sort_function_as (gconstpointer a, gconstpointer b)
{
    const gftp_file * f1, * f2;

    f1 = a;
    f2 = b;
    return (strcasecmp (f1->user, f2->user));
}


static gint
gftp_user_sort_function_ds (gconstpointer a, gconstpointer b)
{
    const gftp_file * f1, * f2;
    gint ret;

    f1 = a;
    f2 = b;
    ret = strcasecmp (f1->user, f2->user);
    return (ret * -1);
}


static gint
gftp_group_sort_function_as (gconstpointer a, gconstpointer b)
{
    const gftp_file * f1, * f2;

    f1 = a;
    f2 = b;
    return (strcasecmp (f1->group, f2->group));
}


static gint
gftp_group_sort_function_ds (gconstpointer a, gconstpointer b)
{
    const gftp_file * f1, * f2;
    gint ret;

    f1 = a;
    f2 = b;
    ret = strcasecmp (f1->group, f2->group);
    return (ret * -1);
}


static gint
gftp_attribs_sort_function_as (gconstpointer a, gconstpointer b)
{
    const gftp_file * f1, * f2;

    f1 = a;
    f2 = b;
    if (f1->st_mode < f2->st_mode)
        return (-1);
    else if (f1->st_mode == f2->st_mode)
        return (0);
    else
        return (1);
}


static gint
gftp_attribs_sort_function_ds (gconstpointer a, gconstpointer b)
{
    const gftp_file * f1, * f2;

    f1 = a;
    f2 = b;
    if (f1->st_mode < f2->st_mode)
        return (1);
    else if (f1->st_mode == f2->st_mode)
        return (0);
    else
        return (-1);
}


static gint
gftp_size_sort_function_as (gconstpointer a, gconstpointer b)
{
    const gftp_file * f1, * f2;

    f1 = a;
    f2 = b;
    if (f1->size < f2->size)
        return (-1);
    else if (f1->size == f2->size)
        return (0);
    else
        return (1);
}


static gint
gftp_size_sort_function_ds (gconstpointer a, gconstpointer b)
{
    const gftp_file * f1, * f2;

    f1 = a;
    f2 = b;
    if (f1->size < f2->size)
        return (1);
    else if (f1->size == f2->size)
        return (0);
    else
        return (-1);
}


static gint
gftp_datetime_sort_function_as (gconstpointer a, gconstpointer b)
{
    const gftp_file * f1, * f2;

    f1 = a;
    f2 = b;
    if (f1->datetime < f2->datetime)
        return (-1);
    else if (f1->datetime == f2->datetime)
        return (0);
    else
        return (1);
}


static gint
gftp_datetime_sort_function_ds (gconstpointer a, gconstpointer b)
{
    const gftp_file * f1, * f2;

    f1 = a;
    f2 = b;
    if (f1->datetime < f2->datetime)
        return (1);
    else if (f1->datetime == f2->datetime)
        return (0);
    else
        return (-1);
}


GList *
gftp_sort_filelist (GList * filelist, int column, int asds)
{
    GList * files, * dirs, * dotdot, * tempitem, * insitem;
    GCompareFunc sortfunc;
    gftp_file * tempfle;
    intptr_t sort_dirs_first;

    if (filelist == NULL) /* nothing to sort */
        return (filelist);

    files = dirs = dotdot = NULL;

    if (column == GFTP_SORT_COL_FILE)
        sortfunc = asds ?  gftp_file_sort_function_as : gftp_file_sort_function_ds;
    else if (column == GFTP_SORT_COL_SIZE)
        sortfunc = asds ?  gftp_size_sort_function_as : gftp_size_sort_function_ds;
    else if (column == GFTP_SORT_COL_USER)
        sortfunc = asds ?  gftp_user_sort_function_as : gftp_user_sort_function_ds;
    else if (column == GFTP_SORT_COL_GROUP)
        sortfunc = asds ?
            gftp_group_sort_function_as : gftp_group_sort_function_ds;
    else if (column == GFTP_SORT_COL_DATETIME)
        sortfunc = asds ?
            gftp_datetime_sort_function_as : gftp_datetime_sort_function_ds;
    else if (column == GFTP_SORT_COL_ATTRIBS)
        sortfunc = asds ? 
            gftp_attribs_sort_function_as : gftp_attribs_sort_function_ds;
    else /* Don't sort */
        return (filelist);

    sort_dirs_first = 1;
    /* gftp_lookup_global_option ("sort_dirs_first", &sort_dirs_first); */

    for (tempitem = filelist; tempitem != NULL; )
    {
        tempfle = tempitem->data;
        insitem = tempitem;
        tempitem = tempitem->next;
        insitem->next = NULL;

        if (dotdot == NULL && strcmp (tempfle->file, "..") == 0)
            dotdot = insitem;
        else if (sort_dirs_first && S_ISDIR (tempfle->st_mode))
        {
            insitem->next = dirs;
            dirs = insitem;
        }
        else
        {
            insitem->next = files;
            files = insitem;
        }
    }

    if (dirs != NULL)
        dirs = g_list_sort (dirs, sortfunc);
    if (files != NULL)
        files = g_list_sort (files, sortfunc);

    filelist = dotdot;

    if (filelist == NULL)
        filelist = dirs;
    else
        filelist = g_list_concat (filelist, dirs);

    if (filelist == NULL)
        filelist = files;
    else
        filelist = g_list_concat (filelist, files);

    /* I haven't check this, but I'm pretty sure some older versions of glib
       had a bug that the prev pointer wasn't being sent to NULL */
    filelist->prev = NULL;
    return (filelist);
}


char *
gftp_gen_ls_string (gftp_request * request, gftp_file * fle,
                    char *file_prefixstr, char *file_suffixstr)
{
    char *tempstr1, *tempstr2, *ret, tstr[50], *attribs, *utf8;
    size_t destlen;
    struct tm *lt;
    time_t t;

    lt = localtime (&fle->datetime);

    attribs = gftp_convert_attributes_from_mode_t (fle->st_mode);
    tempstr1 = g_strdup_printf ("%10s %8s %8s", attribs, fle->user, fle->group);
    g_free (attribs);

    if (GFTP_IS_SPECIAL_DEVICE (fle->st_mode))
        tempstr2 = g_strdup_printf ("%d, %d", major (fle->size), minor (fle->size));
    else
        tempstr2 = g_strdup_printf (GFTP_OFF_T_11PRINTF_MOD, fle->size);

    time (&t);

    if (fle->datetime > t || t - 3600*24*90 > fle->datetime)
        strftime (tstr, sizeof (tstr), "%b %d  %Y", lt);
    else
        strftime (tstr, sizeof (tstr), "%b %d %H:%M", lt);

    if (file_prefixstr == NULL)
        file_prefixstr = "";
    if (file_suffixstr == NULL)
        file_suffixstr = "";

    utf8 = gftp_string_from_utf8 (request, 1, fle->file, &destlen);
    if (utf8 != NULL)
    {
        ret = g_strdup_printf ("%s %s %s %s%s%s", tempstr1, tempstr2, tstr,
                               file_prefixstr, utf8, file_suffixstr);
        g_free (utf8);
    }
    else
        ret = g_strdup_printf ("%s %s %s %s%s%s", tempstr1, tempstr2, tstr,
                               file_prefixstr, fle->file, file_suffixstr);

    g_free (tempstr1);
    g_free (tempstr2);

    return (ret);
}


#if !defined (HAVE_GETADDRINFO) || !defined (HAVE_GAI_STRERROR)

struct hostent *
r_gethostbyname (const char *name, struct hostent *result_buf, int *h_errnop)
{
    static GStaticMutex hostfunclock = G_STATIC_MUTEX_INIT; 
    struct hostent *hent;

    if (g_thread_supported ())
        g_static_mutex_lock (&hostfunclock);

    if ((hent = gethostbyname (name)) == NULL)
    {
        if (h_errnop)
            *h_errnop = h_errno;
    }
    else
    {
        *result_buf = *hent;
        hent = result_buf;
    }

    if (g_thread_supported ())
        g_static_mutex_unlock (&hostfunclock);

    return (hent);
}

#endif /* !HAVE_GETADDRINFO */

struct servent *
r_getservbyname (const char *name, const char *proto,
                 struct servent *result_buf, int *h_errnop)
{
    static GStaticMutex servfunclock = G_STATIC_MUTEX_INIT;
    struct servent *sent;

    if (g_thread_supported ())
        g_static_mutex_lock (&servfunclock);

    if ((sent = getservbyname (name, proto)) == NULL)
    {
        if (h_errnop)
            *h_errnop = h_errno;
    }
    else
    {
        *result_buf = *sent;
        sent = result_buf;
    }

    if (g_thread_supported ())
        g_static_mutex_unlock (&servfunclock);
    return (sent);
}


char *
base64_encode (char *str)
{

/* The standard to Base64 encoding can be found in RFC2045 */

    char *newstr, *newpos, *fillpos, *pos;
    unsigned char table[64], encode[3];
    size_t slen, num;
    int i;

    for (i = 0; i < 26; i++)
    {
        table[i] = 'A' + i;
        table[i + 26] = 'a' + i;
    }

    for (i = 0; i < 10; i++)
        table[i + 52] = '0' + i;

    table[62] = '+';
    table[63] = '/';

    slen = strlen (str);
    num = slen / 3;
    if (slen % 3 > 0)
        num++;

    newstr = g_malloc ((gulong) num * 4 + 1);
    newstr[num * 4] = '\0';
    newpos = newstr;

    pos = str;
    while (*pos != '\0')
    {
        memset (encode, 0, sizeof (encode));
        for (i = 0; i < 3 && *pos != '\0'; i++)
            encode[i] = *pos++;

        fillpos = newpos;
        *newpos++ = table[encode[0] >> 2];
        *newpos++ = table[(encode[0] & 3) << 4 | encode[1] >> 4];
        *newpos++ = table[(encode[1] & 0xF) << 2 | encode[2] >> 6];
        *newpos++ = table[encode[2] & 0x3F];
        while (i < 3)
            fillpos[++i] = '=';
    }
    return (newstr);
}




void
gftp_shutdown (void)
{
    if (gftp_logfd != NULL)
        fclose (gftp_logfd);

    gftp_clear_cache_files ();

    /* if (gftp_configuration_changed) */
    /*     gftp_write_config_file (); */

    exit (0);
}


char *
gftp_build_path (gftp_request * request, const char *first_element, ...) 
{
    const char *element;
    size_t len, retlen;
    int add_separator;
    va_list args;
    char *ret;

    g_return_val_if_fail (first_element != NULL, NULL);

    ret = g_strdup (first_element);
    retlen = strlen (ret);

    va_start (args, first_element);
    for (element = va_arg (args, char *);
         element != NULL;
         element = va_arg (args, char *))
    {
        len = strlen (element);

        if (len == 0)
            continue;

        if (retlen > 0 && (ret[retlen - 1] == '/' || element[0] == '/'))
            add_separator = 0;
        else
        {
            add_separator = 1;
            len++;
        }
      
        retlen += len;
        ret = g_realloc (ret, (gulong) retlen + 1);

        /* Don't append a / for VMS servers... */
        if (add_separator &&
            (request == NULL || request->server_type != GFTP_DIRTYPE_VMS))
            strncat (ret, "/", retlen);

        strncat (ret, element, retlen);
    }

    return (ret);
}



/* Very primary encryption/decryption to make the passwords unreadable
   with 'cat ~/.gftp/bookmarks'.
   
   Each character is separated in two nibbles. Then each nibble is stored
   under the form 01xxxx01. The resulted string is prefixed by a '$'.
*/
char * 
gftp_scramble_password (const char *password)
{
    char *newstr, *newpos;

    if (strcmp (password, "@EMAIL@") == 0)
        return (g_strdup (password));

    newstr = g_malloc ((gulong) strlen (password) * 2 + 2);
    newpos = newstr;
  
    *newpos++ = '$';

    while (*password != '\0')
    {
        *newpos++ = ((*password >> 2) & 0x3c) | 0x41;
        *newpos++ = ((*password << 2) & 0x3c) | 0x41;
        password++;
    }
    *newpos = 0;
  
    return (newstr);
}


char *
gftp_descramble_password (const char *password)
{
    const char *passwordpos;
    char *newstr, *newpos;
    int error;

    if (*password != '$')
        return (g_strdup (password));

    passwordpos = password + 1;
    newstr = g_malloc ((gulong) strlen (passwordpos) / 2 + 1);
    newpos = newstr;
 
    error = 0;
    while (*passwordpos != '\0' && *(passwordpos + 1) != '\0')
    {
        if ((*passwordpos & 0xc3) != 0x41 ||
            (*(passwordpos + 1) & 0xc3) != 0x41)
        {
            error = 1;
            break;
        }

        *newpos++ = ((*passwordpos & 0x3c) << 2) | 
            ((*(passwordpos + 1) & 0x3c) >> 2);

        passwordpos += 2;
    }

    if (error)
    {
        g_free (newstr);
        return (g_strdup (password));
    }

    *newpos = '\0';
    return (newstr);
}
